package com.silverhaze.diamond.client.utils.thread;

import com.silverhaze.diamond.client.Application;
import com.silverhaze.diamond.client.common.AuroraConstants;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * User: Babanin
 * Date: 23.10.2010
 * Time: 2:15:42
 */
public class BackgroundExecutor {
    private static final BackgroundExecutor INSTANCE = new BackgroundExecutor();

    private ExecutorService executorService;
    private Queue<BackgroundTask> activeTasks;
    private Queue<BackgroundTask> tasksToExecute;
    private List<BackgroundExecutorListener> listeners;

    public BackgroundExecutor() {
        listeners = new ArrayList<BackgroundExecutorListener>();
        activeTasks = new LinkedBlockingQueue<BackgroundTask>();
        tasksToExecute = new LinkedBlockingQueue<BackgroundTask>();
        executorService = Executors.newFixedThreadPool(AuroraConstants.THREAD_POOL_SIZE);

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (!activeTasks.isEmpty()) {
                        if (getStatusBar() != null) {
                            getStatusBar().setIndeterminate(true);
                        }

                        Iterator<BackgroundTask> taskIterator = activeTasks.iterator();

                        while (taskIterator.hasNext()) {
                            BackgroundTask task = taskIterator.next();

                            if (task.getFutureResult().isDone() || task.isForceFinished()) {
                                taskIterator.remove();

                                if (task.isCanceled()) {
                                    fireTaskCanceled(task);
                                } else {
                                    if (getStatusBar() != null) {
                                        getStatusBar().setIndeterminate(false);
                                    }

                                    task.fireListeners();
                                    fireTaskFinished(task);
                                }

                                if (!tasksToExecute.isEmpty()) {
                                    BackgroundTask taskToExecute = tasksToExecute.poll();
                                    Future<?> futureResult = executorService.submit(taskToExecute);

                                    taskToExecute.setFutureResult(futureResult);
                                    activeTasks.add(taskToExecute);

                                    fireTaskStarted(taskToExecute);
                                }
                            }
                        }
                    } else {
                        if (getStatusBar() != null) {
                            getStatusBar().setIndeterminate(false);
                        }
                    }

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

    public Future execute(BackgroundTask task) {
        if (activeTasks.size() <= AuroraConstants.THREAD_POOL_SIZE) {
            Future<?> futureResult = executorService.submit(task);

            task.setFutureResult(futureResult);
            activeTasks.add(task);

            fireTaskStarted(task);

            return futureResult;
        } else {
            tasksToExecute.add(task);

            return null;
        }
    }

    public Queue<BackgroundTask> getActiveTasks() {
        return activeTasks;
    }

    public void subscribe(BackgroundExecutorListener listener) {
        if (listener != null && !listeners.contains(listener)) {
            listeners.add(listener);
        }
    }

    public void fireTaskStarted(BackgroundTask task) {
        for (BackgroundExecutorListener listener : listeners) {
            listener.taskStarted(task);
        }
    }

    public void fireTaskCanceled(BackgroundTask task) {
        for (BackgroundExecutorListener listener : listeners) {
            listener.taskCanceled(task);
        }
    }

    public void fireTaskFinished(BackgroundTask task) {
        for (BackgroundExecutorListener listener : listeners) {
            listener.taskFinished(task);
        }
    }

    public void unsubscribe(BackgroundExecutorListener listener) {
        if (listener != null) {
            listeners.remove(listener);
        }
    }

    private JProgressBar getStatusBar() {
        if (Application.getFrame() != null) {
            return Application.getFrame().getProgressBar();
        }

        return null;
    }

    public static BackgroundExecutor getInstance() {
        return INSTANCE;
    }
}